﻿using System;
using System.Collections.Generic;
using System.Linq;
using LoA.Shared.Input;
using LoA.Shared.Interfaces;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace LoA.InputEngine {
	public partial class InputManager : IInputManager {
		public PlayerIndex PlayerIndex { get; private set; }

		public GamePadState PreviousGamePadState { get; private set; }
		public GamePadState GamePadState { get; private set; }
		public KeyboardState PreviousKeyboardState { get; private set; }
		public KeyboardState KeyboardState { get; private set; }
		public Buttons DirectionButtons { get; private set; }

		/// <summary>
		/// The last "real time" that new input was received. Slightly late button
		/// presses will not update this time; they are merged with previous input.
		/// </summary>
		public TimeSpan LastInputTime { get; private set; }

		/// <summary>
		/// The current sequence of pressed buttons.
		/// </summary>
		public List<Buttons> buffer;

		/// <summary>
		/// This is how long to wait for input before all input data is expired.
		/// This prevents the player from performing half of a move, waiting, then
		/// performing the rest of the move after they forgot about the first half.
		/// </summary>
		public readonly TimeSpan BufferTimeOut = TimeSpan.FromMilliseconds(500);

		/// <summary>
		/// This is the size of the "merge window" for combining button presses that
		/// occur at almsot the same time.
		/// If it is too small, players will find it difficult to perform moves which
		/// require pressing several buttons simultaneously.
		/// If it is too large, players will find it difficult to perform moves which
		/// require pressing several buttons in sequence.
		/// </summary>
		public readonly TimeSpan MergeInputTime = TimeSpan.FromMilliseconds(100);

		/// <summary>
		/// Provides the map of non-direction game pad buttons to keyboard keys.
		/// </summary>
		internal static readonly Dictionary<Buttons, Keys> NonDirectionButtons =
			new Dictionary<Buttons, Keys>
            {
                { Buttons.A, Keys.Space },
                { Buttons.B, Keys.Tab },
                { Buttons.X, Keys.F },
                { Buttons.Y, Keys.R },
                // Other available non-direction buttons:
                // Start, Back, LeftShoulder, LeftTrigger, LeftStick,
                // RightShoulder, RightTrigger, and RightStick.
            };

		public InputManager(PlayerIndex playerIndex, int bufferSize) {
			PlayerIndex = playerIndex;
			buffer = new List<Buttons>(bufferSize);
		}

		/// <summary>
		/// Determines if a move matches the current input history. Unless the move is
		/// a sub-move, the history is "consumed" to prevent it from matching twice.
		/// </summary>
		/// <returns>True if the move matches the input history.</returns>
		public bool Matches(MoveSequence move) {
			// If the move is longer than the buffer, it can't possibly match.
			if (buffer.Count < move.Sequence.Length)
				return false;

			// Loop backwards to match against the most recent input.
			for (int i = 1; i <= move.Sequence.Length; ++i) {
				if (buffer[buffer.Count - i] != move.Sequence[move.Sequence.Length - i]) {
					return false;
				}
			}

			// Rnless this move is a component of a larger sequence,
			if (!move.IsSubMove) {
				// consume the used inputs.
				buffer.Clear( );
			}

			return true;
		}

		public bool Matches(Buttons buttons) {
			return (DirectionButtons & buttons) == buttons;
		}
	}
}
